from product.serializers import ProductSerializer
from utils.custom_model_viewSet import CustomModelViewSet
from django_filters import rest_framework as filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
import openpyxl
from django.db import transaction
import io
from datetime import datetime
from system.models import DictData
from utils.models import CommonStatus
from django.apps import apps
import json

# 导入Product模型
Product = apps.get_model('product', 'Product')

# 定义导入功能所需的常量
PRODUCT_REQUIRED_FIELDS = ['sku', 'name']  # 必需的字段

def get_product_status_mapping():
    """从字典系统获取产品状态映射（中文标签 -> 系统值）
    
    用于Excel导入功能中，将中文状态标签转换为系统识别的值
    
    Returns:
        dict: 产品状态映射字典，格式为{label: value, ...}
    """
    # 从字典系统获取产品状态数据
    status_dict_data = DictData.objects.filter(
        dict_type__value='product_status', 
        status=CommonStatus.ENABLED
    ).values('label', 'value')
    
    # 构建映射字典
    mapping = {item['label']: item['value'] for item in status_dict_data}
    
    # 如果字典中没有数据，返回默认映射
    if not mapping:
        mapping = {
            '激活': 'active',
            '非激活': 'inactive',
            '已停产': 'discontinued'
        }
    
    return mapping


class ProductFilter(filters.FilterSet):
    """
    产品 过滤类。用于产品列表过滤。
    """
    # 为常用搜索字段添加模糊搜索支持
    sku = filters.CharFilter(field_name='sku', lookup_expr='icontains')
    name = filters.CharFilter(field_name='name', lookup_expr='icontains')
    spc = filters.CharFilter(field_name='spc', lookup_expr='icontains')
    
    class Meta:
        model = Product
        fields = [
            'product_id', 'product_type', 'creator', 'modifier', 'is_deleted', 
            'sku', 'name', 'spc', 'description', 'status', 
            'category_id', 'unit_pr', 'unit_wh', 'unit_use',
            'sku_supplier', 'barcode', 'inventory_method', 'costing_method',
            'is_batch_tracked', 'is_serial_tracked', 'shelf_life_days',
            'safety_stock', 'reorder_point', 'max_stock', 'default_warehouse_id',
            'default_location', 'preferred_supplier_id', 'purchaser_id', 'lead_time',
            'moq', 'tax_code', 'version'
        ]


class ProductViewSet(CustomModelViewSet):
    """
    产品 视图集，提供产品的增删改查操作。
    """
    queryset = Product.objects.filter(is_deleted=False).order_by('-product_id')
    serializer_class = ProductSerializer
    filterset_class = ProductFilter
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ['sku', 'name', 'description']  # 根据实际字段调整
    ordering_fields = ['create_time', 'product_id', 'sku']
    ordering = ['-create_time']
    # 启用软删除功能
    enable_soft_delete = True
    
    @action(detail=False, methods=['post'], url_path='get-sheet-names')
    def get_sheet_names(self, request, *args, **kwargs):
        """
        获取Excel文件中的所有工作表名称
        
        Args:
            request: HTTP请求对象，包含上传的文件
        
        Returns:
            Response: 包含工作表名称列表的响应
        """
        if 'file' not in request.FILES:
            return Response({
                'code': status.HTTP_400_BAD_REQUEST,
                'message': '请上传Excel文件'
            }, status=status.HTTP_400_BAD_REQUEST)
        
        file = request.FILES['file']
        
        if not file.name.endswith(('.xlsx', '.xls')):
            return Response({
                'code': status.HTTP_400_BAD_REQUEST,
                'message': '请上传Excel格式的文件'
            }, status=status.HTTP_400_BAD_REQUEST)
        
        try:
            wb = openpyxl.load_workbook(io.BytesIO(file.read()), read_only=True)
            sheet_names = wb.sheetnames
            
            # 保存文件到会话中以便后续操作
            request.session['import_file'] = file.read().hex()
            request.session['import_file_name'] = file.name
            
            return Response(sheet_names)
        except Exception as e:
            return Response({
                'code': status.HTTP_500_INTERNAL_SERVER_ERROR,
                'message': f'读取文件失败：{str(e)}'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            
    @action(detail=False, methods=['get'], url_path='get-sheet-headers')
    def get_sheet_headers(self, request, *args, **kwargs):
        """
        获取指定工作表的表头信息
        
        Args:
            request: HTTP请求对象，包含sheetName和startRow参数
        
        Returns:
            Response: 包含表头列表的响应
        """
        sheet_name = request.query_params.get('sheetName')
        start_row = request.query_params.get('startRow', 1)
        
        if not sheet_name:
            return Response({
                'code': status.HTTP_400_BAD_REQUEST,
                'message': '请指定工作表名称'
            }, status=status.HTTP_400_BAD_REQUEST)
        
        try:
            # 从会话中获取文件
            if 'import_file' not in request.session:
                return Response({
                    'code': status.HTTP_400_BAD_REQUEST,
                    'message': '请先上传文件'
                }, status=status.HTTP_400_BAD_REQUEST)
            
            file_content = bytes.fromhex(request.session['import_file'])
            wb = openpyxl.load_workbook(io.BytesIO(file_content), read_only=True)
            
            if sheet_name not in wb.sheetnames:
                return Response({
                    'code': status.HTTP_400_BAD_REQUEST,
                    'message': f'工作表{sheet_name}不存在'
                }, status=status.HTTP_400_BAD_REQUEST)
            
            sheet = wb[sheet_name]
            start_row_num = int(start_row)
            
            # 读取表头行
            header_row = sheet[start_row_num]
            headers = []
            for cell in header_row:
                if cell.value:
                    headers.append(str(cell.value).strip())
                else:
                    headers.append('')
            
            return Response(headers)
        except Exception as e:
            return Response({
                'code': status.HTTP_500_INTERNAL_SERVER_ERROR,
                'message': f'读取表头失败：{str(e)}'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            
    @action(detail=False, methods=['get'], url_path='get-sheet-preview')
    def get_sheet_preview(self, request, *args, **kwargs):
        """
        获取工作表的数据预览
        
        Args:
            request: HTTP请求对象，包含sheetName、startRow和limit参数
        
        Returns:
            Response: 包含预览数据和表头的响应
        """
        sheet_name = request.query_params.get('sheetName')
        start_row = request.query_params.get('startRow', 1)
        limit = request.query_params.get('limit', 5)
        
        if not sheet_name:
            return Response({
                'code': status.HTTP_400_BAD_REQUEST,
                'message': '请指定工作表名称'
            }, status=status.HTTP_400_BAD_REQUEST)
        
        try:
            # 从会话中获取文件
            if 'import_file' not in request.session:
                return Response({
                    'code': status.HTTP_400_BAD_REQUEST,
                    'message': '请先上传文件'
                }, status=status.HTTP_400_BAD_REQUEST)
            
            file_content = bytes.fromhex(request.session['import_file'])
            wb = openpyxl.load_workbook(io.BytesIO(file_content), read_only=True)
            
            if sheet_name not in wb.sheetnames:
                return Response({
                    'code': status.HTTP_400_BAD_REQUEST,
                    'message': f'工作表{sheet_name}不存在'
                }, status=status.HTTP_400_BAD_REQUEST)
            
            sheet = wb[sheet_name]
            start_row_num = int(start_row)
            limit_num = int(limit)
            
            # 读取表头
            header_row = sheet[start_row_num]
            headers = []
            for cell in header_row:
                if cell.value:
                    headers.append(str(cell.value).strip())
                else:
                    headers.append('')
            
            # 读取数据行
            data = []
            max_row = min(start_row_num + limit_num, sheet.max_row + 1)
            for row_idx in range(start_row_num + 1, max_row):
                row_data = {}
                row = sheet[row_idx]
                for i, header in enumerate(headers):
                    if i < len(row) and row[i].value is not None:
                        row_data[header] = str(row[i].value)
                    else:
                        row_data[header] = ''
                data.append(row_data)
            
            return Response({
                'headers': headers,
                'data': data
            })
        except Exception as e:
            return Response({
                'code': status.HTTP_500_INTERNAL_SERVER_ERROR,
                'message': f'读取数据失败：{str(e)}'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            
    @action(detail=False, methods=['post'], url_path='import')
    def import_products(self, request, *args, **kwargs):
        """
        导入产品数据（支持字段映射）
        从Excel文件中导入产品数据到系统，支持用户自定义字段映射
        
        Args:
            request: HTTP请求对象，包含上传的文件、sheetName、startRow、fieldMappings和excelHeaders参数
        
        Returns:
            Response: 包含导入结果的响应对象
        """
        # 从表单数据中获取参数
        sheet_name = request.POST.get('sheetName')
        start_row = request.POST.get('startRow', 1)
        field_mappings_str = request.POST.get('fieldMappings', '[]')
        excel_headers_str = request.POST.get('excelHeaders', '[]')
        
        try:
            field_mappings = json.loads(field_mappings_str)
            excel_headers = json.loads(excel_headers_str)
        except json.JSONDecodeError:
            return Response({
                'code': status.HTTP_400_BAD_REQUEST,
                'message': '字段映射或表头数据格式错误'
            }, status=status.HTTP_400_BAD_REQUEST)
        
        # 如果请求中包含新文件，则使用新文件
        if 'file' in request.FILES:
            file = request.FILES['file']
            if not file.name.endswith(('.xlsx', '.xls')):
                return Response({
                    'code': status.HTTP_400_BAD_REQUEST,
                    'message': '请上传Excel格式的文件'
                }, status=status.HTTP_400_BAD_REQUEST)
            file_content = file.read()
        else:
            # 否则从会话中获取文件
            if 'import_file' not in request.session:
                return Response({
                    'code': status.HTTP_400_BAD_REQUEST,
                    'message': '请先上传文件'
                }, status=status.HTTP_400_BAD_REQUEST)
            file_content = bytes.fromhex(request.session['import_file'])
        
        try:
            wb = openpyxl.load_workbook(io.BytesIO(file_content))
            
            if sheet_name not in wb.sheetnames:
                return Response({
                    'code': status.HTTP_400_BAD_REQUEST,
                    'message': f'工作表{sheet_name}不存在'
                }, status=status.HTTP_400_BAD_REQUEST)
            
            sheet = wb[sheet_name]
            start_row_num = int(start_row)
            
            # 导入数据，使用事务确保原子性
            success_count = 0
            error_count = 0
            error_messages = []
            
            with transaction.atomic():
                # 从表头行的下一行开始读取数据
                max_row = sheet.max_row
                for row_idx in range(start_row_num + 1, max_row + 1):
                    try:
                        # 构建产品数据
                        product_data = {}
                        
                        # 根据字段映射获取数据
                        row = sheet[row_idx]
                        for i, excel_header in enumerate(excel_headers):
                            # 获取当前Excel列对应的系统字段
                            system_field = field_mappings[i] if i < len(field_mappings) else None
                            if system_field and i < len(row):
                                cell_value = row[i].value
                                if cell_value is not None:
                                    product_data[system_field] = cell_value
                        
                        # 验证必需的字段是否存在
                        missing_fields = []
                        for field in PRODUCT_REQUIRED_FIELDS:
                            if field not in product_data or not product_data[field]:
                                missing_fields.append(field)
                        
                        if missing_fields:
                            error_count += 1
                            error_messages.append(f'行{row_idx}：缺少必需字段：{missing_fields}')
                            continue
                        
                        # 处理状态字段的映射
                        if 'status' in product_data:
                            status_mapping = get_product_status_mapping()
                            status_value = product_data['status']
                            if status_value in status_mapping:
                                product_data['status'] = status_mapping[status_value]
                        
                        # 创建或更新产品
                        # 检查是否已存在相同SKU的产品
                        sku = product_data.get('sku')
                        existing_product = Product.objects.filter(sku=sku, is_deleted=False).first()
                        
                        if existing_product:
                            # 更新现有产品
                            for key, value in product_data.items():
                                setattr(existing_product, key, value)
                            existing_product.save()
                        else:
                            # 创建新产品
                            product = Product(**product_data)
                            product.save()
                        
                        success_count += 1
                        
                    except Exception as e:
                        error_count += 1
                        error_messages.append(f'行{row_idx}：处理失败：{str(e)}')
            
            # 清理会话中的文件数据
            if 'import_file' in request.session:
                del request.session['import_file']
            if 'import_file_name' in request.session:
                del request.session['import_file_name']
            
            # 统一返回格式以便前端处理
            return Response({
                'code': status.HTTP_200_OK,
                'message': '导入完成',
                'data': {
                    'success_count': success_count,
                    'fail_count': error_count,
                    'error_messages': error_messages[:10],  # 只返回前10条错误信息
                    'error_report_url': None  # 暂不提供错误报告下载功能
                }
            })
        except Exception as e:
            return Response({
                'code': status.HTTP_500_INTERNAL_SERVER_ERROR,
                'message': f'导入失败：{str(e)}'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
